package se.webbzon.boltzmann.effect;

import se.webbzon.oschi01.resourceset.ResourceSet;
import se.webbzon.oschi01.textureset.Texture3D;
import se.webbzon.oschi01.worldarea.WorldArea;
import se.webbzon.oschi01.worldcamera.WorldCameraPane;
import se.webbzon.oschi01.worldobject.WorldObject;

public abstract class ScreenEffect extends WorldObject implements Effect {
	
	// The 2-dimensional depth of the screen effect
	private static final int depth = -1000;
	
	// The texture set associated with this screen effect
	private final ResourceSet<Texture3D> textureSet;
	
	// The startup duration of the effect
	private final int duration;
		
	// The remaining duration of the effect
	private int remaining;
	
	// The index of the current texture in the texture set
	private double textureIndex;
	
	// The texture update speed for this effect
	private double textureSpeed;
		
	
	/*============================================================
	Constructors
	============================================================*/
	
	/** Creates a new screen effect showing a given set textures. 
	 * The duration (in number of steps) of the effect will 
	 * be equal to the size of the texture set. **/
	public ScreenEffect(ResourceSet<Texture3D> textureSet) {
		this(textureSet,textureSet.size());
	}

	/** Creates a new screen effect showing a given set textures. 
	 * The effect will last for a given amount of time (number of steps).
	 * If the duration is negative then the effect will last until termination. **/
	public ScreenEffect(ResourceSet<Texture3D> textureSet,
						int duration) {
		super(	WorldObject.onStep + WorldObject.onStepEnd + WorldObject.onPostDraw, 
				depth);
		this.textureSet = textureSet;
		this.duration = duration;
		remaining = duration;
		textureIndex = 0;
		textureSpeed = 0;
	}
	
	/*============================================================
	Public Methods
	============================================================*/
	
	/** This method is invoked when the screen effect begins. **/
	public abstract void onEffectBegin(WorldArea world);
	
	/** This method is invoked during each screen effect step. **/
	public abstract void onEffectStep(WorldArea world);
	
	/** This method is invoked when the screen effect ends. **/
	public abstract void onEffectEnd(WorldArea world);
	
	/** This method is invoked when the screen effect renders its
	 * content onto the heads-up display. **/
	public abstract void onEffectDraw(WorldArea world, WorldCameraPane hud);
	
	/** Returns the current texture for this screen effect. **/
	public Texture3D getTexture() {
		return textureSet.get((int) textureIndex);
	}
	
	/** Returns the remaining duration of the effect. **/
	public int getDuration() {
		return remaining;
	}
	
	/** Returns the startup duration of the effect. **/
	public int getStartupDuration() {
		return duration;
	}
	
	/** Returns the update speed of the texture. If speed is equal to
	 * 1 then the texture will be updated once per step. **/
	public double getTextureSpeed() {
		return textureSpeed;
	}
	
	/** Sets the update speed of the texture. If speed is equal to
	 * 1 then the texture will be updated once per step. **/
	public void setTextureSpeed(double imageSpeed) {
		if (imageSpeed >= 0)
			this.textureSpeed = imageSpeed;
	}
	
	/** Returns the index of the current displayed texture.
	 * The real index of the current texture is the returned
	 * value floored. */
	public double getTextureIndex() {
		return textureIndex;
	}
	
	/** Sets the index of the current displayed texture.
	 * The real index of the current texture is the set
	 * value floored. */
	public void setTextureIndex(double textureIndex) {
		if (	textureIndex != this.textureIndex && 
				textureIndex >= 0 && 
				textureIndex < textureSet.size()) {
			this.textureIndex = textureIndex;
		}
	}

	@Override public final void launch(WorldArea world) {
		world.createInstance(this);
		onEffectBegin(world);
	}

	@Override public final void terminate(WorldArea world) {
		onEffectEnd(world);
		world.destroyInstance(this);
	}
	
	@Override public final void onStep(WorldArea world) {
		if (remaining == 0)
			terminate(world);
		else if (remaining > 0) {
			onEffectStep(world);
			remaining--;
		}
	}
	
	@Override public final void onStepEnd(WorldArea world) {
		if (textureSpeed > 0) {
			double textureIndex = this.textureIndex + textureSpeed;
			if (textureIndex >= textureSet.size())
				textureIndex -= textureSet.size();
			setTextureIndex(textureIndex);
		}
	}
	
	@Override public final void onPostDraw(WorldArea world, WorldCameraPane hud) {
		onEffectDraw(world,hud);
	}
	

}
